/**
 *  Copyright (C) 2008  Huang Guan
 *  Copyright (C) 2011  iBoxpay.com inc.
 *
 *  $Id: 691029ec2ac041372193855b2eb56db17bdac132 $
 *
 *  Description: This file mainly includes the functions about utf8
 *
 *  History:
 *  2008-7-10 13:31:57 Created.
 *  2011-12-28 Format the code style, and add comments by Lytsing
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#ifdef __WIN32__
#include <windows.h>
#else
#include <iconv.h>
#endif

#include <errno.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "utf8.h"

#ifdef __WIN32__
void utf8_to_gb(const char* src, char* dst, int len) {
    int ret = 0;
    WCHAR* strA;
    int i = MultiByteToWideChar(CP_UTF8, 0, src, -1, NULL, 0);
    if (i <= 0) {
        printf("ERROR.");
        return;
    }
    strA = (WCHAR*)malloc(i * 2);
    MultiByteToWideChar(CP_UTF8, 0, src, -1, strA, i);
    i = WideCharToMultiByte(CP_ACP, 0, strA, -1, NULL, 0, NULL, NULL);
    if (len >= i) {
        ret = WideCharToMultiByte(CP_ACP, 0, strA, -1, dst, i, NULL, NULL);
        dst[i] = 0;
    }
    if (ret <= 0) {
        free(strA);
        return;
    }

    free(strA);
}

void gb_to_utf8(const char* src, char* dst, int len) {
    int ret = 0;
    WCHAR* strA;
    int i = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
    if (i <= 0) {
        printf("ERROR.");
        return;
    }
    strA = (WCHAR*)malloc(i * 2);
    MultiByteToWideChar(CP_ACP, 0, src, -1, strA, i);
    i = WideCharToMultiByte(CP_UTF8, 0, strA, -1, NULL, 0, NULL, NULL);
    if (len >= i) {
        ret = WideCharToMultiByte(CP_UTF8, 0, strA, -1, dst, i, NULL, NULL);
        dst[i] = 0;
    }

    if (ret <= 0) {
        free(strA);
        return;
    }
    free(strA);
}
#else  // Linux
// starkwong: In iconv implementations, inlen and outlen should be type of
// size_t not uint, which is different in length on Mac
void utf8_to_gb(const char* src, char* dst, int len) {
    int ret = 0;
    size_t inlen = strlen(src) + 1;
    size_t outlen = len;

    // duanqn: The iconv function in Linux requires non-const char *
    // So we need to copy the source string
    char* inbuf = (char*)malloc(len);
    char* inbuf_hold = inbuf;  // iconv may change the address of inbuf
                               // so we use another pointer to keep the address
    memcpy(inbuf, src, len);

    char* outbuf = dst;
    iconv_t cd;
    cd = iconv_open("GBK", "UTF-8");
    if (cd != (iconv_t)-1) {
        ret = iconv(cd, &inbuf, &inlen, &outbuf, &outlen);
        if (ret != 0) {
            printf("iconv failed err: %s\n", strerror(errno));
        }

        iconv_close(cd);
    }
    free(inbuf_hold);  // Don't pass in inbuf as it may have been modified
}

/**
 * 将 GBK 字符串转换为 UTF-8
 *
 * @param src 要转换的 GBK 字符串
 * @param dst 接收 UTF-8 结果的缓冲区
 * @param dst_len dst 缓冲区的最大容量 (必须足够大)
 * @return 成功返回转换后的字节数 (不含 \0)，失败返回 -1
 */
int gb_to_utf8(const char* src, char* dst, size_t dst_len) {
    // 1. 检查输入/输出缓冲区是否有效
    if (!src || !dst || dst_len == 0) return -1;

    iconv_t cd;
    size_t inlen, outlen;
    char *inbuf_ptr, *outbuf_ptr;
    char* inbuf_copy;
    int ret = -1;

    // 2. 计算输入长度并分配临时内存
    // 必须包含空终止符 \0，因为我们要让 iconv 处理它
    inlen = strlen(src) + 1;

    // 拷贝输入字符串以满足 iconv 对非 const 指针的要求
    // 分配空间必须等于实际输入字符串的长度 (inlen)
    inbuf_copy = (char*)malloc(inlen);
    if (!inbuf_copy) {
        perror("malloc failed for inbuf");
        return -1;
    }
    memcpy(inbuf_copy, src, inlen);

    // 3. 准备 iconv 指针
    inbuf_ptr = inbuf_copy;
    outbuf_ptr = dst;
    outlen = dst_len;  // 初始输出可用长度

    // 4. 处理 src == dst 的情况 (原地转换)
    char* temp_outbuf = NULL;
    size_t temp_outlen = dst_len;  // 假设调用者传入的 dst_len 足够

    if (src == dst) {
        // 如果是原地转换，必须分配一个临时的、足够大的缓冲区
        // 转换后长度可能增加 1.5 倍，我们用 2 倍来确保安全
        temp_outlen = inlen * 2 > dst_len ? inlen * 2 : dst_len;
        temp_outbuf = (char*)malloc(temp_outlen);
        if (!temp_outbuf) {
            perror("malloc failed for temp_outbuf");
            free(inbuf_copy);
            return -1;
        }
        outbuf_ptr = temp_outbuf;
        outlen = temp_outlen;
    }

    // 5. 打开转换器
    cd = iconv_open("UTF-8", "GBK");
    if (cd == (iconv_t)-1) {
        perror("iconv_open failed");
        goto cleanup;
    }

    // 6. 执行转换
    // 注意：iconv 会修改 inbuf_ptr, inlen, outbuf_ptr, outlen
    size_t converted = iconv(cd, &inbuf_ptr, &inlen, &outbuf_ptr, &outlen);

    if (converted == (size_t)-1) {
        printf("iconv failed. Remaining input: %zu, errno: %d (%s)\n", inlen,
               errno, strerror(errno));
        ret = -1;  // 转换失败
    } else {
        // 7. 成功：在输出的末尾手动添加空终止符 \0
        // outbuf_ptr 指向下一个写入位置
        if (outlen > 0) {
            *outbuf_ptr = '\0';
            ret = (int)(dst_len - outlen);  // 返回转换的字节数
        } else {
            // 即使转换成功，如果最后没有空间写入 \0 也是失败
            printf("iconv success but no space for null terminator.\n");
            ret = -1;
        }
    }

    // 8. 处理原地转换的收尾工作
    if (temp_outbuf) {
        if (ret != -1) {
            // 成功：将临时缓冲区内容复制回 dst
            size_t final_len = (size_t)ret + 1;  // 实际长度 + \0
            if (final_len > dst_len) {
                // 如果临时缓冲区比原始 dst 缓冲区大，并且结果超出了原始 dst_len
                // 此时应该返回错误或截断，这里我们假设原始 dst_len 是安全上限
                final_len = dst_len;
            }
            memcpy(dst, temp_outbuf, final_len);
            dst[dst_len - 1] = '\0';  // 确保目标缓冲区总是以 \0 结尾
        }
        free(temp_outbuf);
    }

cleanup:
    // 9. 清理资源
    if (cd != (iconv_t)-1) iconv_close(cd);
    free(inbuf_copy);

    return ret;
}
#endif
